<?php
class M_user extends CI_Model {

	/**
	* This array contains the following:
	* id			The id of the user.
	* username		The username of the user.
	* password		The user's encrypted password.
	* email		The user's e-mail address.
	* signup_date	The datetime when the user was registred.
	* salt			The basic salt of the user.
	* 
	* @var array Contains the information about the user
	*/
	var $info = array(); //Array with user information.
	
	function __construct()
	{
		parent::__construct();
	}
	
	/**
	 * Function for encrypting the user password.
	 *
	 * @param string password The unecrypted password of the user.
	 * @return string The encrypted password in form of a 128 character long hash.
	 */
	private function hash_password($password)
	{
		//Fetching the salt unique for every implementation of the system.
		$site_salt = $this->config->item('password_salt');
		
		//Fetching the whole salt for the user.
		$user_salt = $this->generate_salt();
		
		//Creating a basic hash of the password and the salts.
		$hash = hash('sha512', sha1($user_salt . $password . $site_salt) . hash('whirlpool', $site_salt . $password . $user_salt) . md5($user_salt . $password . $site_salt));
		
		//Reencrypt the hash several times to provide higher security.
		for ($i = 0; $i < 10000; $i++)
			$hash = hash('sha512', sha1($hash) . hash('whirlpool', $hash) . md5($hash));
		
		//Returning the encrypted password.
		return $hash;
	}
	
	/**
	 * Function for generating the salt for the user that are to be used in the password encryption algorithm.
	 * This salt contains username, signup date and the basic user salt.
	 *
	 * @param void
	 * @return string The salt for the user.
	 */
	private function generate_salt()
	{
		return $this->info['salt'] . $this->info['signup'] . $this->info['username'];
	}
	
	/**
	 * Function for checking if a user with a certain identifier (username or email) already exists in the database.
	 *
	 * @param string identifier The identifier which are to be checked.
	 * @param string type The type of the identifier (username or email).
	 * @return bool TRUE if a user with the identifier already exists, FALSE if it does NOT exist.
	 */
	private function user_exists($identifier, $type)
	{
		//Fetching all rows with the identifier to check if the user exist.
		$query = $this->db->get_where('user', array($type => $identifier));
		
		//Check that there are not too many or too few users with the identifier.
		if ($query->num_rows() > 1) {
			//If there are two users with the same identifier something is realy wrong.
			die("Something went terrible wrong! Error 0001.");
		}
		elseif ($query->num_rows() < 1) {
			//Return that there are NO such user.
			return FALSE;
		}
		else {
			//Means num_rows == 1.
			return TRUE; //Return that there ARE such an user.
		}
	}
	
	/**
	 * Function that gets the user id from an identifier of the user (username or email).
	 *
	 * @param string identifier The identifier for which's id are to be fetched.
	 * @param string type The type of the identifier (username or email).
	 * @return int user_id The id of the user with the sent identifier.
	 */
	private function get_id($identifier, $type)
	{
		//Create the database query.
		$this->db->select('id');
		$this->db->from('user');
		$this->db->where($type, $identifier);
		
		//Fetching data about the user.
		$query = $this->db->get();
		$result = $query->result_array();
		
		//Returning the user id of selected user name.
		return $result[0]['id'];
	}
	
	/**
	 * Function for loading the information about the user from the database into the user object.
	 *
	 * @param string username The username of the user who's information are to be loaded.
	 * @return void
	 */
	function load_userdata($user_id)
	{
		//Create the database query.
		$this->db->select('id, username, password, email, level, salt, signup, activation');
		$this->db->from('user');
		$this->db->where('id', $user_id);
		
		//Fetching data about the user.
		$query = $this->db->get();
		
		//Making sure that there actually exist a challenge with the sent id.
		if ($query->num_rows() != 1) {
			die("Something went terrible wrong! Error 0002.");
		}
		
		//Loading data into the user object.
		$result = $query->result_array();
		$this->info = $result[0];
	}
	
	/* -------------------------------------------------------------
	Resend functions.
	------------------------------------------------------------- */
	
	/**
	 * Function for resending the username.
	 *
	 * @param string email The email of the user who's username is to be resent.
	 * @return int The result of the resending. 0 = Successful, 1 = Bad email address.
	 */
	function resend_username($email)
	{
		//Check if the user actually exists.
		if (!$this->user_exists($email, 'email')) {
			return 1; //Bad email.
		}
		
		//Fetching user id.
		$user_id = $this->get_id($email, 'email');
		
		//Loading user data into the user objekt.
		$this->load_userdata($user_id);
		
		//Send the mail with the username.
		$this->send_username_mail();
		
		return 0;
	}
	
	/**
	 * Function for sending out the username for an account.
	 *
	 * @param void
	 * @return void
	 */
	private function send_username_mail()
	{
		//Get username.
		$username = $this->info['username'];
		
		//Load email library.
		$this->load->library('email');
		
		//Creating message content.
		$sender		= "Namustar's administrators";
		$subject	= 'The username for your Namustar account';
		$message	= "Someone (probably you) have requested us to send you your username for your Numastar account." . PHP_EOL . PHP_EOL . "Your username is $username." . PHP_EOL . PHP_EOL . "Best regards" . PHP_EOL . $sender;
		
		//Creating the email.
		$this->email->from('info@example.com', $sender);
		$this->email->to($this->info['email']); 
		$this->email->subject($subject);
		$this->email->message($message);
		
		//Send activation email.
		$this->email->send();
	}
	
	/**
	 * Function for sending out reset-password-link.
	 *
	 * @param string username The username of the user who's password is to be resent.
	 * @return int The result of the resending. 0 = Successful, 1 = Bad username.
	 */
	public function resend_password($username)
	{
		//Check if the user actually exists.
		if (!$this->user_exists($username, 'username')) {
			return 1; //Bad username.
		}
		
		//Fetching user id.
		$user_id = $this->get_id($username, 'username');
		
		//Loading user data into the user objekt.
		$this->load_userdata($user_id);
		
		//Getting the number of password resettings for selected user in the database (should be 0 or 1).
		$this->db->where('user_id', $user_id);
		$this->db->where('type_id', 1);
		$query = $this->db->get('user_update');
		
		//Find out if there already are a password resetting pending.
		if ($query->num_rows() > 1) {
			die("Something went terrible wrong! Error 0003.");
		}
		
		//Setting resetting information.
		$reset_data = array(	'user_id'		=> $user_id,
								'type_id'		=> 1, //Password resetting.
								'key'			=> sha1(mt_rand()),
								'new_value'		=> '', //Not necessary for resetting passwords.
								'update_date'	=> date('Y-m-d H:i:s') );
								
		
		if ($query->num_rows() == 1) {
			//Updating the current key.
			$this->db->where('user_id', $user_id);
			$this->db->where('type_id', 1);
			
			$this->db->update('user_update', $reset_data); 
		}
		else {
			//Inserting new key for resetting password.
			$this->db->insert('user_update', $reset_data); 
		}
		
		//Send link for resetting password.
		$this->send_password_mail($reset_data['key']);
		
		return 0;
	}
	
	/**
	 * Function for sending out the password reset link for an user account.
	 *
	 * @param string reset_key The reset key used to verify the user.
	 * @return void
	 */
	private function send_password_mail($reset_key)
	{
		//Get username.
		$username = $this->info['username'];
		
		//Load email library.
		$this->load->library('email');
		
		//Creating message content.
		$reset_link	= site_url() . '/update/password/' . $this->info['username'] . '/' . $reset_key;
		$sender		= "Namustar's administrators";
		$subject	= 'Reset password to your Namustar account';
		$message	= "Someone (probably you) have requested a new password for your Numastar account. To set a new password, go to the adress $reset_link" . PHP_EOL . "If you can't click the at the adress, you should copy and paste it into your web browser." . PHP_EOL . "If it was not you doing the password reset you could ignore this mail and nothing will happen." . PHP_EOL . PHP_EOL . "Best regards" . PHP_EOL . $sender;
		
		//Creating the email.
		$this->email->from('info@example.com', $sender);
		$this->email->to($this->info['email']); 
		$this->email->subject($subject);
		$this->email->message($message);
		
		//Send activation email.
		$this->email->send();
	}
	
	/* -------------------------------------------------------------
	Functions about activation/inactivation of account.
	------------------------------------------------------------- */
	
	/**
	 * Function for activating the user account.
	 *
	 * @param string username The username of the user that are to be activated.
	 * @param string activation_code The activation code for the user.
	 * @return int The activation result. 0 = Successful, 1 = Bad username, 2 = Already activated, 3 = Bad activation code.
	 */
	function activate($username, $activation_code)
	{
		//Check if the user actually exists.
		if (!$this->user_exists($username, 'username')) {
			return 1; //Bad username.
		}
		
		//Fetching user id.
		$user_id = $this->get_id($username, 'username');
		
		//Loading user data into the user objekt.
		$this->load_userdata($user_id);
		
		//Check if the account is already activated.
		if ($this->info['activation'] == FALSE) {
			return 2; //Already activated.
		}
		
		//Check if the activation code is correct.
		if ($this->info['activation'] === $activation_code) {
			//Do activation.
			$data = array('activation' => '');
			$this->db->where('id', $user_id);
			$this->db->update('user', $data); 
			
			return 0; //Successful.
		}
		else {
			return 3; //Bad activation code.
		}
	}
	
	/**
	 * Function for resending the activation code.
	 *
	 * @param string username The username of the user who's activation code is to be resent.
	 * @return int The result of the resending. 0 = Successful, 1 = Bad username, 2 = Already activated.
	 */
	function resend_activation($username)
	{
		//Check if the user actually exists.
		if (!$this->user_exists($username, 'username')) {
			return 1; //Bad username.
		}
		
		//Fetching user id.
		$user_id = $this->get_id($username, 'username');
		
		//Loading user data into the user objekt.
		$this->load_userdata($user_id);
		
		//Check if the account is already activated.
		if ($this->info['activation'] == FALSE) {
			return 2; //Already activated.
		}
		
		//Send new activation link.
		$this->send_activation_mail();
		
		return 0;
	}
	
	/**
	 * Function for sending out the activation link for the account to the user.
	 *
	 * @param void
	 * @return void
	 */
	function send_activation_mail()
	{
		//Load email library.
		$this->load->library('email');
		
		//Creating message content.
		$activation_link	= site_url() . '/update/activation/' . $this->info['username'] . '/' . $this->info['activation'];
		$sender				= "Namustar's administrators";
		$subject			= 'Activation link to your Namustar account';
		$message			= "Thanks for registrating at Namustar!" . PHP_EOL . "To log on you have to activate your account. This is done by going to the adress $activation_link" . PHP_EOL . "If you can't click the at the adress, you should copy and paste it into your web browser." . PHP_EOL . "If it was not you doing the registration you could ignore this mail and the account will be deleted." . PHP_EOL . PHP_EOL . "Best regards" . PHP_EOL . $sender;
		
		//Creating the email.
		$this->email->from('info@example.com', $sender);
		$this->email->to($this->info['email']); 
		$this->email->subject($subject);
		$this->email->message($message);
		
		//Send activation email.
		$this->email->send();
	}
	
	/* -------------------------------------------------------------
	Information fetching functions.
	------------------------------------------------------------- */
	
	/**
	 * Function for returning information about the user.
	 *
	 * @param array parameters An array containing the names of the element to be returned from the user object variable.
	 * @return mixed Requested information about the user.
	 */
	function get_info($parameters)
	{
		//If no parameters was sent, then return false and abort.
		if (count($parameters) == 0) {
			return FALSE;
		}
		
		//Return false if the info array is empty (the object isn't loaded).
		if (empty($this->info)) {
			return FALSE;
		}
		
		//Creating an array for the return values.
		$return = array();
		
		//Going thru the parameters to see what to return.
		foreach ($parameters as $p)
		{
			//If the parameter sent correspond to a value in the object variabel, then that value is loaded into the return array.
			switch ($p)
			{
				case 'all_info':
					return $this->info;
					break;
				case 'id':
					$return['id'] = $this->info['id'];				
					break;
				case 'username':
					$return['username'] = $this->info['username'];
					break;
				case 'password':
					$return['password'] = $this->info['password'];
					break;
				case 'email':
					$return['email'] = $this->info['email'];
					break;
				case 'level':
					$return['level'] = $this->info['level'];
					break;
				case 'salt':
					$return['salt'] = $this->info['salt'];
					break;
				case 'signup':
					$return['signup'] = $this->info['signup'];
					break;
				case 'activation':
					$return['activation'] = $this->info['activation'];
					break;
				default:
					return FALSE; //If an incorrect index is sent, then return false and abort.
					break;
			}
		}
		
		//If only one parameter is sent, then return only that value.
		if (count($return) == 1) {
			return $return[$parameters[0]];
		}
		
		//Returning values asked for.
		return $return;
	}
	
	/**
	 * Function that returns information about all users.
	 *
	 * @param array parameters An array containing the names of the element to be returned.
	 * @return array return An array containing the requested information about the users.
	 */
	function get_all($parameters)
	{
		//Create the database query.
			//Creating a string variabel that are to contain selected elements in a form that can be used in CodeIgniter function.
			$select = '';
			
			//Putting all selected elements into the string.
			foreach ($parameters as $p) {
				$select .= "$p, ";
			}
			
			//Trimming the string so it can be used in CodeIgniter function.
			trim($select, ", ");
			
			//Creating the query.
			$this->db->select($select);
			$this->db->from('users');
		//Stop creating the database query.
		
		//Fetching data about the user.
		$query = $this->db->get();
		
		//Return the result array containing the user information.
		return $query->result_array();
	}
	
	/* -------------------------------------------------------------
	Create/Delete-functions.
	------------------------------------------------------------- */
	
	/**
	 * Function for creating a user.
	 *
	 * @param array parameters Array containing the information about the user that are to be created.
	 * @return bool TRUE if the user was created, else FALSE.
	 */
	function create($parameters)
	{
		//Check that essential parameters are sent.
		if (	!isset($parameters['username']) ||
				!isset($parameters['password']) ||
				!isset($parameters['email']) ) {
			return FALSE;
		}
		
		//Loading parameters into variabels.
		$username	= $parameters['username'];
		$password	= $parameters['password'];
		$email		= $parameters['email'];
		
		//Check that the username isn't already taken!
		if ($this->user_exists($username, 'username')) {
			return FALSE;
		}
		
		//Loading userdata into the data array of the user object.
		$this->info['username']		= $username;
		$this->info['email']		= $email;
		$this->info['level']		= 1; //Standard user
		$this->info['salt']			= sha1(mt_rand());
		$this->info['signup']		= date('Y-m-d H:i:s');
		$this->info['activation']	= sha1(mt_rand());
		$this->info['password']		= $this->hash_password($password); //hash_password() is using the salt, signup and username, and must therefore be placed after those.
		
		//Inserting the new user into the database.
		$this->db->insert('user', $this->info);
		
		//Loading the user id into the data array of the user object.
		$this->info['id'] = $this->db->insert_id();
		
		//Send a activation mail to the user.
		$this->send_activation_mail();
		
		//Returning that the creation was successful.
		return TRUE;
	}
	
	/**
	 * Function for deleting a user.
	 *
	 * @param void
	 * @return bool TRUE if the user was deleted, else FALSE.
	 */
	function delete()
	{
		
		
		return FALSE;
	}
	
	/* -------------------------------------------------------------
	Functions for changing user info.
	------------------------------------------------------------- */
	
	/**
	 * Function for changing the password of an user.
	 *
	 * @param string new_password The new password for the user.
	 * @return void
	 */
	function change_password($new_password)
	{
		//Encrypting the new password.
		$new_password = $this->hash_password($new_password);
		
		//Getting the username.
		$username = $this->info['username'];
		
		//Creating an array with the user information that are to be updated.
		$data = array('password' => $new_password);
		
		//Updating the user password.
		$this->db->where('username', $username);
		$this->db->update('users', $data);
	}
	
	/* -------------------------------------------------------------
	Login/Logout-functions.
	------------------------------------------------------------- */
	
	/**
	 * Function for logging in a user.
	 *
	 * @param string username The username entered by the user.
	 * @param string password Unencrypted password entered by the user.
	 * @return int The login result. 0 = Successful, 1 = Not activated, 2 = Bad username, 3 = Bad password.
	 */
	function login($username, $password)
	{
		//Check if the username even exists.
		if (!$this->user_exists($username, 'username')) {
			return 2; //Bad username.
		}
		
		//Fetching user id.
		$user_id = $this->get_id($username, 'username');
		
		//Loading user data into the user objekt.
		$this->load_userdata($user_id);
		
		//Encrypting the entered password.
		$password = $this->hash_password($password);
		
		//Checking if the password is exactly the same as the password in the database.
		if ($this->info['password'] === $password)
		{
			//Check if the account is activated.
			if ($this->info['activation'] != FALSE) { //If the activation contains something then the acount is inactive.
				return 1; //Not activated.
			}
			else { //The account is activated.
				//Do logging in.
				$this->session->set_userdata('user_logedin', TRUE);
				$this->session->set_userdata('user_userid', $this->info['id']);
				
				return 0; //Successful.
			}
		}
		else
			return 3; //Bad password.
	}
	
	/**
	 * Function for logging out a user.
	 *
	 * @param void
	 * @return void
	 */
	function logout()
	{
		$this->session->unset_userdata('user_logedin');
		$this->session->unset_userdata('user_userid');
	}
}